package com.rules.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gillion.grule.client.core.variable.*;
import com.gillion.grule.client.utils.Closure;
import com.gillion.grule.client.utils.Streams;

import com.google.common.collect.Lists;
import com.rules.admin.dao.RuleVariableDao;
import com.rules.admin.entity.*;
import com.rules.admin.entity.RuleVariable;
import com.rules.admin.service.RulePackageService;
import com.rules.admin.service.RuleVariableService;
import com.rules.admin.service.RuleAssemblyService;
import com.rules.admin.service.RuleService;
import com.rules.admin.service.decision.DecisionTableService;
import com.rules.admin.service.DecisionTreeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Service
public class RuleVariableServiceImpl extends ServiceImpl<RuleVariableDao, RuleVariable> implements RuleVariableService {
   @Autowired
   private RulePackageService rulePackageService;
   @Autowired
   private RuleAssemblyService ruleAssemblyService;
   @Autowired
   private RuleService ruleService;
   @Autowired
   private DecisionTableService decisionTableService;
   @Autowired
   private DecisionTreeService decisionTreeService;

   public PackageVariable loadCompletelyPackageVaribles(Long packageId) {
      PackageVariable packageVariable = new PackageVariable();
      GlobalVariable globalVariable = this.loadGlobalVariable();
      packageVariable.setGlobalVariable(globalVariable);
      this.loadPackageVariable(packageId, packageVariable);
      this.loadAssemblyVariables(packageId, packageVariable);
      this.loadDecisionVariables(packageId, packageVariable);
      this.loadDecisionTreeVariables(packageId, packageVariable);
      return packageVariable;
   }

   private void loadDecisionTreeVariables(Long packageId, final PackageVariable packageVariable) {

      List<DecisionTree> trees = this.decisionTreeService.getDecisionTreesByPackageId(packageId);
      Streams.of(trees).forEach(new Closure<DecisionTree>() {
         public void execute(DecisionTree decisionTree) {
            final DecisionTreeVariable decisionVariable = new DecisionTreeVariable();
            packageVariable.getDecisionTreeVariables().put(decisionTree.getName(), decisionVariable);
            LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(RuleVariable::getScope, 5).eq(RuleVariable::getScopeId, decisionTree.getId());
            decisionVariable.setName(decisionTree.getName());
            decisionVariable.setPackageVariable(packageVariable);
            List<RuleVariable> ruleVariables = RuleVariableServiceImpl.this.list(queryWrapper);
            Streams.of(ruleVariables).forEach(new Closure<RuleVariable>() {
               public void execute(RuleVariable ruleVariable) {
                  decisionVariable.getVariables().put(ruleVariable.getVarName(), ruleVariable.getVarValueExpr());
               }
            });
         }
      });
   }

   private void loadDecisionVariables(Long packageId, final PackageVariable packageVariable) {

      List<DecisionTable> tables = this.decisionTableService.getDecisionTablesByPackageId(packageId);
      Streams.of(tables).forEach(new Closure<DecisionTable>() {
         public void execute(DecisionTable decisionTable) {
            final DecisionVariable decisionVariable = new DecisionVariable();
            packageVariable.getDecisionVariables().put(decisionTable.getName(), decisionVariable);

            LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(RuleVariable::getScope, 4).eq(RuleVariable::getScopeId, decisionTable.getId());
            decisionVariable.setName(decisionTable.getName());
            decisionVariable.setPackageVariable(packageVariable);
            List<RuleVariable> ruleVariables = RuleVariableServiceImpl.this.list(queryWrapper);
            Streams.of(ruleVariables).forEach(new Closure<RuleVariable>() {
               public void execute(RuleVariable ruleVariable) {
                  decisionVariable.getVariables().put(ruleVariable.getVarName(), ruleVariable.getVarValueExpr());
               }
            });
         }
      });
   }

   public List<RuleVariable> loadAssemblyVariablesById(Long assemblyId) {
      List<RuleVariable> variables = new ArrayList();
      List<RuleVariable> assemblyVariables = this.getByScopeAndScopeId(2, assemblyId);
      RuleAssembly assembly = this.ruleAssemblyService.getById(assemblyId);
      RulePackage rulePackage = this.rulePackageService.getById(assembly.getPackageId());
      List<RuleVariable> packageVariables = this.getByScopeAndScopeId(1, rulePackage.getId());
      variables.addAll(packageVariables);
      variables.addAll(assemblyVariables);
      return variables;
   }

   public List<RuleVariable> loadRuleVariablesById(Long ruleId) {
      List<RuleVariable> variables = new ArrayList();
      List<RuleVariable> ruleVariables = this.getByScopeAndScopeId(3, ruleId);
      Rule rule = this.ruleService.getById(ruleId);
      RuleAssembly assembly = this.ruleAssemblyService.getById(rule.getRuleAssemblyId());
      List<RuleVariable> assemblyVariables = this.getByScopeAndScopeId(2, assembly.getId());
      RulePackage rulePackage = this.rulePackageService.getById(assembly.getPackageId());
      List<RuleVariable> packageVariables = this.getByScopeAndScopeId(1, rulePackage.getId());
      variables.addAll(packageVariables);
      variables.addAll(assemblyVariables);
      variables.addAll(ruleVariables);
      return variables;
   }

   private void loadAssemblyVariables(Long packageId, final PackageVariable packageVariable) {

      List<RuleAssembly> ruleAssemblies = this.ruleAssemblyService.getRuleAssemblyByPackageId(packageId);
      Streams.of(ruleAssemblies).forEach(new Closure<RuleAssembly>() {
         public void execute(RuleAssembly ruleAssembly) {
            AssemblyVariable assemblyVariable = RuleVariableServiceImpl.this.loadAssemblyVariables(ruleAssembly);
            packageVariable.getAssemblyVariables().put(ruleAssembly.getName(), assemblyVariable);
            RuleVariableServiceImpl.this.loadRuleVariables(ruleAssembly, assemblyVariable);
         }
      });
   }

   private void loadRuleVariables(RuleAssembly ruleAssembly, final AssemblyVariable assemblyVariable) {

      List<Rule> rules = this.ruleService.getRuleListByAssemblyId(ruleAssembly.getId());
      Streams.of(rules).forEach(new Closure<Rule>() {
         public void execute(Rule rule) {
            String ruleName = rule.getName();
            LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(RuleVariable::getScope, 3).eq(RuleVariable::getScopeId, rule.getId());

            final com.gillion.grule.client.core.variable.RuleVariable ruleVariable = new com.gillion.grule.client.core.variable.RuleVariable();
            ruleVariable.setName(ruleName);
            assemblyVariable.getRuleVariables().put(ruleName, ruleVariable);
            List<RuleVariable> ruleVariables = RuleVariableServiceImpl.this.list(queryWrapper);
            Streams.of(ruleVariables).forEach(new Closure<RuleVariable>() {
               public void execute(RuleVariable rv) {
                  ruleVariable.getVariables().put(rv.getVarName(), rv.getVarValueExpr());
               }
            });
         }
      });
   }

   private AssemblyVariable loadAssemblyVariables(RuleAssembly ruleAssembly) {
      final AssemblyVariable assemblyVariable = new AssemblyVariable();
      LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.eq(RuleVariable::getScope, 2).eq(RuleVariable::getScopeId, ruleAssembly.getId());

      assemblyVariable.setName(ruleAssembly.getName());
      List<RuleVariable> assemblyVariables = this.list(queryWrapper);
      Streams.of(assemblyVariables).forEach(new Closure<RuleVariable>() {
         public void execute(RuleVariable ruleVariable) {
            assemblyVariable.getVariables().put(ruleVariable.getVarName(), ruleVariable.getVarValueExpr());
         }
      });
      return assemblyVariable;
   }

   private void loadPackageVariable(Long packageId, final PackageVariable packageVariable) {

      LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.eq(RuleVariable::getScope, 1).eq(RuleVariable::getScopeId, packageId);


      RulePackage rulePackage = this.rulePackageService.getById(packageId);
      packageVariable.setName(rulePackage.getName());
      List<RuleVariable> packageVariables = this.list(queryWrapper);
      Streams.of(packageVariables).forEach(new Closure<RuleVariable>() {
         public void execute(RuleVariable ruleVariable) {
            if (ruleVariable.getValueType() == 3) {
               Object obj = null;

               try {
                  obj = Class.forName(ruleVariable.getVarValueExpr()).newInstance();
               } catch (Exception var4) {
                  var4.printStackTrace();
               }
               packageVariable.getVariables().put(ruleVariable.getVarName(), obj);
            } else if (ruleVariable.getValueType() == 0) {
               if (ruleVariable.getVarValueExpr().contains(".")) {
                  packageVariable.getVariables().put(ruleVariable.getVarName(), new BigDecimal(ruleVariable.getVarValueExpr()));
               } else {
                  packageVariable.getVariables().put(ruleVariable.getVarName(), Integer.valueOf(ruleVariable.getVarValueExpr()));
               }
            } else {
               packageVariable.getVariables().put(ruleVariable.getVarName(), ruleVariable.getVarValueExpr());
            }

         }
      });
   }

   private GlobalVariable loadGlobalVariable() {
      final GlobalVariable globalVariable = new GlobalVariable();

      LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.eq(RuleVariable::getScope, 0);
      List<RuleVariable> globalVariables = this.baseMapper.selectList(queryWrapper);
      Streams.of(globalVariables).forEach(new Closure<RuleVariable>() {
         public void execute(RuleVariable ruleVariable) {
            globalVariable.getVariables().put(ruleVariable.getVarName(), ruleVariable.getVarValueExpr());
         }
      });
      return globalVariable;
   }

   public List<RuleVariable> loadPackageVariables(int scope, long scopeId) {
      LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.eq(RuleVariable::getScope, scope).eq(RuleVariable::getScopeId, scopeId);
      return this.list(queryWrapper);
   }

   public List<RuleVariable> getByScopeAndScopeId(int scope, Long scopeId) {
      LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.eq(RuleVariable::getScope, scope).eq(RuleVariable::getScopeId, scopeId);
      return this.list(queryWrapper);
   }

   public void deleteVariables(int scope, Long scopeId) {
      LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.eq(RuleVariable::getScope, scope).eq(RuleVariable::getScopeId, scopeId);
      this.remove(queryWrapper);
   }

   public List<RuleVariable> loadDecisionTableVariables(Long tableId) {
      List<RuleVariable> variables = Lists.newArrayList();
      DecisionTable decisionTable = this.decisionTableService.getById(tableId);
      RulePackage rulePackage = this.rulePackageService.getById(decisionTable.getRulePackageId());
      List<RuleVariable> packageVariables = this.getByScopeAndScopeId(1, rulePackage.getId());
      List<RuleVariable> decisionVariables = this.getByScopeAndScopeId(4, tableId);
      variables.addAll(packageVariables);
      variables.addAll(decisionVariables);
      return variables;
   }

   public List<RuleVariable> loadDecisionTreeVariables(Long tableId) {
      List<RuleVariable> variables = Lists.newArrayList();
      DecisionTree decisionTree = this.decisionTreeService.getById(tableId);
      RulePackage rulePackage = this.rulePackageService.getById(decisionTree.getRulePackageId());
      List<RuleVariable> packageVariables = this.getByScopeAndScopeId(1, rulePackage.getId());
      List<RuleVariable> decisionVariables = this.getByScopeAndScopeId(5, tableId);
      variables.addAll(packageVariables);
      variables.addAll(decisionVariables);
      return variables;
   }

   @Override
   public List<RuleVariable> getByScopeId(Long scopeId) {
      LambdaQueryWrapper<RuleVariable> queryWrapper = new LambdaQueryWrapper<>();
      queryWrapper.eq(RuleVariable::getScopeId, scopeId);
      return this.list(queryWrapper);
   }
}
